[[...path]].page.tsx 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  1. import React from 'react';
  2. import { isClient, objectIdUtils } from '@growi/core';
  3. import {
  4. NextPage, GetServerSideProps, GetServerSidePropsContext,
  5. } from 'next';
  6. import { useTranslation } from 'next-i18next';
  7. import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
  8. import dynamic from 'next/dynamic';
  9. import { useRouter } from 'next/router';
  10. import { Container, Provider } from 'unstated';
  11. import AdminAppContainer from '~/client/services/AdminAppContainer';
  12. import AdminBasicSecurityContainer from '~/client/services/AdminBasicSecurityContainer';
  13. import AdminCustomizeContainer from '~/client/services/AdminCustomizeContainer';
  14. import AdminExternalAccountsContainer from '~/client/services/AdminExternalAccountsContainer';
  15. import AdminGeneralSecurityContainer from '~/client/services/AdminGeneralSecurityContainer';
  16. import AdminGitHubSecurityContainer from '~/client/services/AdminGitHubSecurityContainer';
  17. import AdminGoogleSecurityContainer from '~/client/services/AdminGoogleSecurityContainer';
  18. import AdminHomeContainer from '~/client/services/AdminHomeContainer';
  19. import AdminImportContainer from '~/client/services/AdminImportContainer';
  20. import AdminLdapSecurityContainer from '~/client/services/AdminLdapSecurityContainer';
  21. import AdminLocalSecurityContainer from '~/client/services/AdminLocalSecurityContainer';
  22. import AdminMarkDownContainer from '~/client/services/AdminMarkDownContainer';
  23. import AdminNotificationContainer from '~/client/services/AdminNotificationContainer';
  24. import AdminOidcSecurityContainer from '~/client/services/AdminOidcSecurityContainer';
  25. import AdminSamlSecurityContainer from '~/client/services/AdminSamlSecurityContainer';
  26. import AdminSlackIntegrationLegacyContainer from '~/client/services/AdminSlackIntegrationLegacyContainer';
  27. import AdminTwitterSecurityContainer from '~/client/services/AdminTwitterSecurityContainer';
  28. import AdminUsersContainer from '~/client/services/AdminUsersContainer';
  29. import { SupportedActionType } from '~/interfaces/activity';
  30. import { CrowiRequest } from '~/interfaces/crowi-request';
  31. import PluginUtils from '~/server/plugins/plugin-utils';
  32. import ConfigLoader from '~/server/service/config-loader';
  33. import {
  34. useCurrentUser, /* useSearchServiceConfigured, */ useIsAclEnabled, useIsMailerSetup, useIsSearchServiceReachable, useSiteUrl,
  35. useAuditLogEnabled, useAuditLogAvailableActions,
  36. } from '~/stores/context';
  37. import {
  38. CommonProps, getServerSideCommonProps, getNextI18NextConfig,
  39. } from '../utils/commons';
  40. // import { useEnvVars } from '~/stores/admin-context';
  41. const AdminHome = dynamic(() => import('../../components/Admin/AdminHome/AdminHome'), { ssr: false });
  42. const AppSettingsPageContents = dynamic(() => import('../../components/Admin/App/AppSettingsPageContents'), { ssr: false });
  43. const SecurityManagement = dynamic(() => import('../../components/Admin/Security/SecurityManagement'), { ssr: false });
  44. const MarkDownSettingContents = dynamic(() => import('../../components/Admin/MarkdownSetting/MarkDownSettingContents'), { ssr: false });
  45. const CustomizeSettingContents = dynamic(() => import('../../components/Admin/Customize/Customize'), { ssr: false });
  46. const DataImportPageContents = dynamic(() => import('../../components/Admin/ImportData/ImportDataPageContents'), { ssr: false });
  47. const ExportArchiveDataPage = dynamic(() => import('../../components/Admin/ExportArchiveDataPage'), { ssr: false });
  48. const NotificationSetting = dynamic(() => import('../../components/Admin/Notification/NotificationSetting'), { ssr: false });
  49. const ManageGlobalNotification = dynamic(() => import('../../components/Admin/Notification/ManageGlobalNotification'), { ssr: false });
  50. const SlackIntegration = dynamic(() => import('../../components/Admin/SlackIntegration/SlackIntegration'), { ssr: false });
  51. const LegacySlackIntegration = dynamic(() => import('../../components/Admin/LegacySlackIntegration/LegacySlackIntegration'), { ssr: false });
  52. const UserManagement = dynamic(() => import('../../components/Admin/UserManagement'), { ssr: false });
  53. const ManageExternalAccount = dynamic(() => import('../../components/Admin/ManageExternalAccount'), { ssr: false });
  54. const ElasticsearchManagement = dynamic(() => import('../../components/Admin/ElasticsearchManagement/ElasticsearchManagement'), { ssr: false });
  55. const UserGroupDetailPage = dynamic(() => import('../../components/Admin/UserGroupDetail/UserGroupDetailPage'), { ssr: false });
  56. const AdminLayout = dynamic(() => import('../../components/Layout/AdminLayout'), { ssr: false });
  57. // named export
  58. const UserGroupPage = dynamic(() => import('../../components/Admin/UserGroup/UserGroupPage').then(mod => mod.UserGroupPage), { ssr: false });
  59. const AuditLogManagement = dynamic(() => import('../../components/Admin/AuditLogManagement').then(mod => mod.AuditLogManagement), { ssr: false });
  60. const pluginUtils = new PluginUtils();
  61. type Props = CommonProps & {
  62. currentUser: any,
  63. nodeVersion: string,
  64. npmVersion: string,
  65. yarnVersion: string,
  66. installedPlugins: any,
  67. envVars: any,
  68. isAclEnabled: boolean,
  69. isSearchServiceConfigured: boolean,
  70. isSearchServiceReachable: boolean,
  71. isMailerSetup: boolean,
  72. auditLogEnabled: boolean,
  73. auditLogAvailableActions: SupportedActionType[],
  74. siteUrl: string,
  75. };
  76. const AdminMarkdownSettingsPage: NextPage<Props> = (props: Props) => {
  77. const { t } = useTranslation('admin');
  78. const router = useRouter();
  79. const { path } = router.query;
  80. const pagePathKeys: string[] = Array.isArray(path) ? path : ['home'];
  81. /*
  82. * Set userGroupId as a adminPagesMap key
  83. * eg) In case that url is `/user-group-detail/62e8388a9a649bea5e703ef7`, userGroupId will be 62e8388a9a649bea5e703ef7
  84. */
  85. let userGroupId;
  86. const [firstPath, secondPath] = pagePathKeys;
  87. if (firstPath === 'user-group-detail') {
  88. userGroupId = objectIdUtils.isValidObjectId(secondPath) ? secondPath : undefined;
  89. }
  90. // TODO: refactoring adminPagesMap => https://redmine.weseek.co.jp/issues/102694
  91. const adminPagesMap = {
  92. home: {
  93. title: t('wiki_management_home_page'),
  94. component: <AdminHome
  95. nodeVersion={props.nodeVersion}
  96. npmVersion={props.npmVersion}
  97. yarnVersion={props.yarnVersion}
  98. installedPlugins={props.installedPlugins}
  99. />,
  100. },
  101. app: {
  102. title: t('app_settings'),
  103. component: <AppSettingsPageContents />,
  104. },
  105. security: {
  106. title: t('security_settings.security_settings'),
  107. component: <SecurityManagement />,
  108. },
  109. markdown: {
  110. title: t('markdown_settings.markdown_settings'),
  111. component: <MarkDownSettingContents />,
  112. },
  113. customize: {
  114. title: t('customize_settings.customize_settings'),
  115. component: <CustomizeSettingContents />,
  116. },
  117. importer: {
  118. title: t('importer_management.import_data'),
  119. component: <DataImportPageContents />,
  120. },
  121. export: {
  122. title: t('export_archive_data'),
  123. component: <ExportArchiveDataPage />,
  124. },
  125. notification: {
  126. title: t('external_notification.external_notification'),
  127. component: <NotificationSetting />,
  128. },
  129. 'global-notification': {
  130. new: {
  131. title: t('external_notification.external_notification'),
  132. component: <ManageGlobalNotification />,
  133. },
  134. },
  135. 'slack-integration': {
  136. title: t('slack_integration.slack_integration'),
  137. component: <SlackIntegration />,
  138. },
  139. 'slack-integration-legacy': {
  140. title: t('slack_integration_legacy.slack_integration_legacy'),
  141. component: <LegacySlackIntegration />,
  142. },
  143. users: {
  144. title: t('user_management.user_management'),
  145. component: <UserManagement />,
  146. 'external-accounts': {
  147. title: t('user_management.external_account'),
  148. component: <ManageExternalAccount />,
  149. },
  150. },
  151. 'user-groups': {
  152. title: t('user_group_management.user_group_management'),
  153. component: <UserGroupPage />,
  154. },
  155. 'user-group-detail': {
  156. [userGroupId]: {
  157. title: t('user_group_management.user_group_management'),
  158. component: <UserGroupDetailPage userGroupId={userGroupId} />,
  159. },
  160. },
  161. search: {
  162. title: t('full_text_search_management.full_text_search_management'),
  163. component: <ElasticsearchManagement />,
  164. },
  165. 'audit-log': {
  166. title: t('audit_log_management.audit_log'),
  167. component: <AuditLogManagement />,
  168. },
  169. };
  170. const getTargetPageToRender = (pagesMap, keys): {title: string, component: JSX.Element} => {
  171. return keys.reduce((pagesMap, key) => {
  172. return pagesMap[key];
  173. }, pagesMap);
  174. };
  175. const targetPage = getTargetPageToRender(adminPagesMap, pagePathKeys);
  176. useCurrentUser(props.currentUser != null ? JSON.parse(props.currentUser) : null);
  177. useIsMailerSetup(props.isMailerSetup);
  178. // useSearchServiceConfigured(props.isSearchServiceConfigured);
  179. useIsSearchServiceReachable(props.isSearchServiceReachable);
  180. useIsAclEnabled(props.isAclEnabled);
  181. useSiteUrl(props.siteUrl);
  182. // useEnvVars(props.envVars);
  183. useAuditLogEnabled(props.auditLogEnabled);
  184. useAuditLogAvailableActions(props.auditLogAvailableActions);
  185. const injectableContainers: Container<any>[] = [];
  186. if (isClient()) {
  187. // Create unstated container instances (except Security)
  188. const adminAppContainer = new AdminAppContainer();
  189. const adminImportContainer = new AdminImportContainer();
  190. const adminHomeContainer = new AdminHomeContainer();
  191. const adminCustomizeContainer = new AdminCustomizeContainer();
  192. const adminUsersContainer = new AdminUsersContainer();
  193. const adminExternalAccountsContainer = new AdminExternalAccountsContainer();
  194. const adminNotificationContainer = new AdminNotificationContainer();
  195. const adminSlackIntegrationLegacyContainer = new AdminSlackIntegrationLegacyContainer();
  196. const adminMarkDownContainer = new AdminMarkDownContainer();
  197. injectableContainers.push(
  198. adminAppContainer,
  199. adminImportContainer,
  200. adminHomeContainer,
  201. adminCustomizeContainer,
  202. adminUsersContainer,
  203. adminExternalAccountsContainer,
  204. adminNotificationContainer,
  205. adminSlackIntegrationLegacyContainer,
  206. adminMarkDownContainer,
  207. );
  208. }
  209. const adminSecurityContainers: Container<any>[] = [];
  210. if (isClient()) {
  211. const adminSecuritySettingElem = document.getElementById('admin-security-setting');
  212. if (adminSecuritySettingElem != null) {
  213. // Create unstated container instances (Security)
  214. const adminGeneralSecurityContainer = new AdminGeneralSecurityContainer();
  215. const adminLocalSecurityContainer = new AdminLocalSecurityContainer();
  216. const adminLdapSecurityContainer = new AdminLdapSecurityContainer();
  217. const adminSamlSecurityContainer = new AdminSamlSecurityContainer();
  218. const adminOidcSecurityContainer = new AdminOidcSecurityContainer();
  219. const adminBasicSecurityContainer = new AdminBasicSecurityContainer();
  220. const adminGoogleSecurityContainer = new AdminGoogleSecurityContainer();
  221. const adminGitHubSecurityContainer = new AdminGitHubSecurityContainer();
  222. const adminTwitterSecurityContainer = new AdminTwitterSecurityContainer();
  223. adminSecurityContainers.push(
  224. adminGeneralSecurityContainer,
  225. adminLocalSecurityContainer,
  226. adminLdapSecurityContainer,
  227. adminSamlSecurityContainer,
  228. adminOidcSecurityContainer,
  229. adminBasicSecurityContainer,
  230. adminGoogleSecurityContainer,
  231. adminGitHubSecurityContainer,
  232. adminTwitterSecurityContainer,
  233. );
  234. }
  235. }
  236. return (
  237. <Provider inject={[...injectableContainers, ...adminSecurityContainers]}>
  238. <AdminLayout title={targetPage.title} selectedNavOpt={firstPath}>
  239. {targetPage.component}
  240. </AdminLayout>
  241. </Provider>
  242. );
  243. };
  244. async function injectServerConfigurations(context: GetServerSidePropsContext, props: Props): Promise<void> {
  245. const req: CrowiRequest = context.req as CrowiRequest;
  246. const { crowi } = req;
  247. const {
  248. appService, mailService, aclService, searchService, activityService,
  249. } = crowi;
  250. props.siteUrl = appService.getSiteUrl();
  251. props.nodeVersion = crowi.runtimeVersions.versions.node ? crowi.runtimeVersions.versions.node.version.version : null;
  252. props.npmVersion = crowi.runtimeVersions.versions.npm ? crowi.runtimeVersions.versions.npm.version.version : null;
  253. props.yarnVersion = crowi.runtimeVersions.versions.yarn ? crowi.runtimeVersions.versions.yarn.version.version : null;
  254. props.installedPlugins = pluginUtils.listPlugins();
  255. props.envVars = await ConfigLoader.getEnvVarsForDisplay(true);
  256. props.isAclEnabled = aclService.isAclEnabled();
  257. props.isSearchServiceConfigured = searchService.isConfigured;
  258. props.isSearchServiceReachable = searchService.isReachable;
  259. props.isMailerSetup = mailService.isMailerSetup;
  260. props.auditLogEnabled = crowi.configManager.getConfig('crowi', 'app:auditLogEnabled');
  261. props.auditLogAvailableActions = activityService.getAvailableActions(false);
  262. }
  263. /**
  264. * for Server Side Translations
  265. * @param context
  266. * @param props
  267. * @param namespacesRequired
  268. */
  269. async function injectNextI18NextConfigurations(context: GetServerSidePropsContext, props: Props, namespacesRequired?: string[] | undefined): Promise<void> {
  270. const nextI18NextConfig = await getNextI18NextConfig(serverSideTranslations, context, namespacesRequired);
  271. props._nextI18Next = nextI18NextConfig._nextI18Next;
  272. }
  273. export const getServerSideProps: GetServerSideProps = async(context: GetServerSidePropsContext) => {
  274. const req: CrowiRequest = context.req as CrowiRequest;
  275. const { user } = req;
  276. const result = await getServerSideCommonProps(context);
  277. // check for presence
  278. // see: https://github.com/vercel/next.js/issues/19271#issuecomment-730006862
  279. if (!('props' in result)) {
  280. throw new Error('invalid getSSP result');
  281. }
  282. const props: Props = result.props as Props;
  283. if (user != null) {
  284. // props.currentUser = JSON.stringify(user.toObject());
  285. props.currentUser = JSON.stringify(user);
  286. }
  287. injectServerConfigurations(context, props);
  288. await injectNextI18NextConfigurations(context, props, ['admin']);
  289. return {
  290. props,
  291. };
  292. };
  293. export default AdminMarkdownSettingsPage;